from functools import partial
from PyQt5.QtWidgets import (QPushButton, QTableWidgetItem, QHeaderView, 
                             QFrame, QHBoxLayout, QMessageBox, QDialog)

from data.iotype import IO_TYPE
from frw.vacwx import IVacWidget, IVacDialog
# from frw.wx.menu_bar import MenuBar
from data.parser import load_config_file, dump_to_turning_yaml


class InsertDialogVac(IVacDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        # self.buttonBox.accepted.connect(accept)

    def accept(self):
        if not ( self.key_addr.text() and self.value_addr.text()):
            QMessageBox.warning(self, "错误", "未完整填写输入/输出数据")
            return 0
        # 需要检查当前页面，如PLC输入页面，则需要根据当前环境添加check_infomation ??

        self.list_insert_info = [
            self.key_addr.text(),
            self.value_addr.text(),
            self.intro.toPlainText(),
            False if self.nouse.isChecked() else True
        ]
        super().accept()


class EditorMenuBar(IVacWidget):
    def __init__(self, parent=None, uifile=None):  # uifile="frw/ui/menu_bar.ui"
        self.parent = parent
        super().__init__(parent, uifile)

    def menu_bars_init(self, list_btns_name_info):
        """ info的结构为： [("btn_name": list_submenu_btns_name), ... ] """
        self.list_menu_btn_name = []  # 在self.on_menu_btn_clicked()中调用
        for index, tuple_info in enumerate(list_btns_name_info):
            btn_name, list_submenu_btns_name = tuple_info
            self.list_menu_btn_name.append(btn_name)
            # 创建按钮
            btn_wx = QPushButton(btn_name)  # , self.menu_btn_frame
            btn_wx.clicked.connect(partial(self.on_menu_btn_clicked,
                    index, False if list_submenu_btns_name else True))
            self.menus_layout.addWidget(btn_wx)

            # 根据当前的menu_btn，初始化sub_title
            self.submenu_bars_init(list_submenu_btns_name, index)

    def on_menu_btn_clicked(self, menu_btn_index, toHide=False):
        if toHide:
            self.submenubar_frame.hide()
            return

        self.submenubar_frame.setCurrentIndex(menu_btn_index)
        self.parent.table_wx.on_table_filter_prefix(self.list_menu_btn_name[menu_btn_index])
        self.submenubar_frame.show()

    def submenu_bars_init(self, list_btns_id, stackwx_index=None):
        """ name的结构为 ["btn_name", ... ] """
        panel_frame = QFrame()
        panel_layout = QHBoxLayout()
        panel_frame.setLayout(panel_layout)

        if stackwx_index:
            self.submenubar_frame.insertWidget(stackwx_index, panel_frame)
        else:
            self.submenubar_frame.addWidget(panel_frame)

        for sub_btn_name in list_btns_id:
            submenu_btn = QPushButton(sub_btn_name)
            submenu_btn.clicked.connect(
                    partial(self.on_submenu_btn_clicked, sub_btn_name))
            # 默认将按钮添加，并隐藏
            panel_layout.addWidget(submenu_btn)

    def on_submenu_btn_clicked(self, btn_info):
        self.parent.table_wx.on_table_filter_subprefix(btn_info)


class TableManager:
    """ 负责以下功能：
          - 控制表格控件的显示状态
          - 与数据库的交互（导入/查找/导出）
    """
    def __init__(self, table_widget):
        # self.maps_changed = False  # 用于记录是否改动文件，并在退出编辑模式时选择是否写入文件
        self.maybe_modifier = ""  # 利用 self.maybe_modifier 变量判断是否为修改，或是刷新表格

        self.table_wx = table_widget
        self._active()

    def _active(self):
        self.table_wx.itemDoubleClicked.connect(self.on_table_itemDoubleClicked)  # 记录修改前cell内容
        self.table_wx.itemChanged.connect(self.on_table_itemChanged)  # 数据变更触发回调函数

    def import_data(self, config_path):
        self.config_path = config_path
        self.data_db = load_config_file(self.config_path)  # 创建新的数据库表

        self.table_data_update(None)
        self.table_showing_style()

    def table_showing_style(self):
        header = self.table_wx.horizontalHeader()
        # 此处根据具体需要，选择是否需要逐行定义
        for column_index in range(self.len_ui_table_column):
            header.setSectionResizeMode(column_index, QHeaderView.Stretch)  # ResizeToContents

    def table_data_update(self, list_data_items):
        """ list_data_items comes from DataBase.maps_export(),
            the format is like this: (xx, xx, xx, xx)
        """
        # UI设置表格
        self.table_wx.clearContents()
        self.table_wx.setRowCount(0)
        if list_data_items is None:
            list_data_items = self.data_db.maps_export_all()  # maps_export()??
        if not list_data_items:  return
        self.len_ui_table_column = len(list_data_items[0])

        for row_index, tuple_map in enumerate(list_data_items):
            self.table_wx.insertRow(row_index)  # 动态添加行
            for column_index in range(self.len_ui_table_column):
                # 动态写入单元格数据
                if column_index == 3:
                    item_value = "True" if tuple_map[column_index] else "False"
                else:
                    item_value = tuple_map[column_index]
                self.table_wx.setItem(row_index, column_index, QTableWidgetItem(item_value))

    def on_table_filter_keyword(self, keyword):
        sql_like = "%{}%".format(keyword)
        list_fields = []  # ["key_addr", "value_addr", "intro"]
        for index, field in enumerate(self.data_db.tb_maps[0:3]):
            list_fields[index] = field + " LIKE '{}'".format(sql_like)
        SQL = " OR ".join(list_fields)
        list_db_maps = self.data_db.maps_export(where=SQL)
        self.table_data_update(list_db_maps)

    def on_table_filter_prefix(self, btn_name):
        prefix_choices = IO_TYPE.dict_name2prefix[btn_name]  # it's a LIST
        if len(prefix_choices) == 1:
            sql_where = "key_main_prefix='{}'".format(prefix_choices[0])
        else:
            multi_prefix_choices = "'" + "','".join(prefix_choices) + "'"
            sql_where = "key_main_prefix in ({})".format(multi_prefix_choices)
        list_db_maps = self.data_db.maps_export(where=sql_where)
        self.table_data_update(list_db_maps)

    def on_table_filter_subprefix(self, sub_btn_name):
        prefix_choices = IO_TYPE.dict_name2prefix[sub_btn_name]
        if len(prefix_choices) == 1:
            sql_where = "key_main_prefix='{0}' OR key_sub_prefix='{0}' ".format(prefix_choices[0])
        else:
            multi_prefix_choices = "'" + "','".join(prefix_choices) + "'"
            sql_where = "key_main_prefix in ({0}) OR key_sub_prefix in ({0})".format(multi_prefix_choices)
        list_db_maps = self.data_db.maps_export(where=sql_where)
        self.table_data_update(list_db_maps)

    def on_table_filter_untype(self):
        sql_where = "key_untype = 1"
        list_db_maps = self.data_db.maps_export(where=sql_where)
        self.table_data_update(list_db_maps)

    def on_table_insertRow(self, list_insert_data):
        index_row_insert = self.table_wx.currentRow()  # 当前行上方插入
        if index_row_insert < 0:  # -1: 光标不在table控件上
            index_row_insert = self.table_wx.rowCount()  # 末行插入

        self.table_wx.insertRow(index_row_insert)
        for column_index in range(self.len_ui_table_column):
            # 动态写入单元格数据
            if column_index == 3:
                item_value = "True" if list_insert_data[column_index] else "False"
            else:
                item_value = list_insert_data[column_index]
            self.table_wx.setItem(index_row_insert, column_index, QTableWidgetItem(item_value))
        self.data_db.maps_insert(list_insert_data)
        dump_to_turning_yaml(self.data_db, self.config_path)

    def on_table_removeRow(self):
        """ 由于MAP表中不存在主键（允许nouse内容完全相同），所以删除内容时，可能会将同样内容的行一并删除 """
        index_curr_row = self.table_wx.currentRow()

        list_line = []  # 需要全部数据作为主键，传入查询数据库
        for index_column in range(self.len_ui_table_column):
            cell_content = self.table_wx.item(index_curr_row, index_column).text()
            if index_column == 3:
                cell_content = "1" if "True" == cell_content else "0"
            list_line.append(cell_content)
        self.table_wx.removeRow(index_curr_row)
        # self.maps_changed = True
        self.data_db.maps_delete(list_line)
        dump_to_turning_yaml(self.data_db, self.config_path)

    def on_table_itemDoubleClicked(self, table_item):
        self.maybe_modifier = table_item.text()

    def on_table_itemChanged(self, table_item):
        """ 用于处理表格修改的Signal:
            void QTableWidget::itemChanged(QTableWidgetItem *item)
        """
        if not self.maybe_modifier:  return  # 未双击

        tb_item_row = table_item.row()
        tb_item_column = table_item.column()
        tb_item_value = table_item.text()

        if self.maybe_modifier == tb_item_value:  return

        # check the table_item value:
        if tb_item_column == 3 and tb_item_value not in ("True", "False"):
            # raise Exception("EditorPageVac::on_table_itemChanged() -->> Nonuse列只允许写入 “True” or “False”")
            QMessageBox.about(self, "警告", "Nonuse列只允许写入 “True” or “False”")
            # 回写先前值
            table_item.setText(self.maybe_modifier)
            return

        # 改写数据库
        if tb_item_column == 0:  # 改写主键
            tb_item_key = self.maybe_modifier
        else:
            tb_item_key = self.table_wx.item(tb_item_row, 0).text()
        self.data_db.maps_update(tb_item_column, tb_item_key, tb_item_value, self.maybe_modifier)

        # self.maps_changed = True
        self.maybe_modifier = ""  # 清空双击时的预选内容
        dump_to_turning_yaml(self.data_db, self.config_path)

    """
    def save_to_config(self):  # export from the sqlite3
        # 退出编辑模式时，将数据库信息反写入文件
        if not self.maps_changed:  return

        status = QMessageBox.question(self, "警告", "尚未保存当前信息，是否保存？")
        if status == QMessageBox.No:  return

        dump_to_turning_yaml(self.data_db, self.config_path)
        self.maps_changed = False
    """


class EditorPageVac(IVacWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # self.list_title_btn_name = []  # ["CNC", "PLC", "GPIO"]
        self.menu_bar_wx = EditorMenuBar(self)
        self.menu_layout.addWidget(self.menu_bar_wx)
        self.menu_bar_init()

        self.table_wx = TableManager(self.tableWidget)
        self._active()

    def import_config(self, config_path):
        self.table_wx.import_data(config_path)
        # 切换到首页（"显示全部"）
        self._on_filter_none_btn_clicked()

    def menu_bar_init(self):
        # UI的title两级菜单，与IO_Pin的prefix并不直接对等，而是利用IO_Type.dict_name2prefix实现
        # - 利用IO_TYPE.dict_name确定menu_bar中按钮的name
        # - 利用IO_TYPE.dict_name2prefix（即得到的prefix）确定过滤的内容
        list_btns_name_info = []
        for main_type, dict_subtype in IO_TYPE.dict_name.items():
            list_submenu_btns_name = []
            # 第一级菜单，使用 IO_TYPE.dict_name
            for sub_type in dict_subtype.keys():  # 由于UI只显示两层菜单，故忽略sub-subtype...
                list_submenu_btns_name.append(sub_type)
            tuple_info = (main_type, list_submenu_btns_name)
            list_btns_name_info.append(tuple_info)

        self.menu_bar_wx.menu_bars_init(list_btns_name_info)

    def _on_filter_none_btn_clicked(self):
        self.menu_bar_wx.on_menu_btn_clicked(None, True)  # 隐藏submenu_bar
        self.table_wx.table_data_update(None)

    def _active(self):
        # active the static buttons:
        menu_bar = self.menu_bar_wx

        def on_filter_others_btn_clicked():
            menu_bar.on_menu_btn_clicked(None, True)  # 隐藏submenu_bar
            self.table_wx.on_table_filter_untype()

        menu_bar.filter_others_btn.clicked.connect(on_filter_others_btn_clicked)
        menu_bar.filter_none_btn.clicked.connect(self._on_filter_none_btn_clicked)
        self._menu_bar_extend_btns_active()

    def _menu_bar_extend_btns_active(self):
        menu_bar = self.menu_bar_wx

        def on_search_edit_clicked():
            key_word = menu_bar.search_edit.text()
            if not key_word:  return
            self.table_wx.on_table_filter_keyword(key_word)

        def on_insert_btn_clicked():
            dialog = InsertDialogVac(self)
            if dialog.exec() == QDialog.Accepted:
                self.table_wx.on_table_insertRow(dialog.list_insert_info)

        menu_bar.search_edit.returnPressed.connect(on_search_edit_clicked)
        menu_bar.insert_btn.clicked.connect(on_insert_btn_clicked)
        menu_bar.remove_btn.clicked.connect(self.table_wx.on_table_removeRow)
